/*
 * UMediaPacketQueue.cpp
 *
 *  Created on: 2020年4月25日
 *      Author: chuanjiang.zh
 */

#include "UMediaPacketQueue.h"
#include <deque>
#include "TCriticalSection.h"
#include "TEvent.h"


typedef std::deque< UMediaPacket >  PacketDeque;


class UMediaPacketQueuePrivate
{
public:
    PacketDeque m_packets;
    comn::CriticalSection   m_cs;
    comn::Event	m_event;
    bool m_eventEnabled;

    UMediaPacketQueuePrivate():
        m_eventEnabled(true)
    {
    }

    size_t findVideoKey()
    {
    	for (size_t i = 0; i < m_packets.size(); i ++)
    	{
    		UMediaPacket& pkt = m_packets[i];
    		if (pkt.isVideo() && pkt.isKey())
    		{
    			return i;
    		}
    	}
    	return -1;
    }

    size_t rfindVideoKey()
    {
    	if (m_packets.size() < 2)
    	{
    		return -1;
    	}

    	for (size_t i = m_packets.size() - 1; i != 0; i --)
    	{
    		UMediaPacket& pkt = m_packets[i];
    		if (pkt.isVideo() && pkt.isKey())
    		{
    			return i;
    		}
    	}
    	return -1;
    }


    size_t size()
    {
        comn::AutoCritSec   lock(m_cs);
        return m_packets.size();
    }

    bool empty()
    {
        comn::AutoCritSec   lock(m_cs);
        return m_packets.empty();
    }

    size_t push(UMediaPacket& packet)
    {
        size_t count = 0;
        {
            comn::AutoCritSec   lock(m_cs);
            m_packets.push_back(packet);

            count = m_packets.size();
        }

        if (m_eventEnabled)
        {
            m_event.post();
        }

        return count;
    }

    UMediaPacket pop()
    {
        UMediaPacket packet;
        pop(packet);
        return packet;
    }

    bool pop(UMediaPacket& packet)
    {
        bool found = false;
        comn::AutoCritSec   lock(m_cs);
        if (!m_packets.empty())
        {
            packet = m_packets.front();
            m_packets.pop_front();
            found = true;
        }
        return found;
    }

    void clear()
    {
        {
            comn::AutoCritSec   lock(m_cs);
            m_packets.clear();
        }

        m_event.post();
    }

    bool timedwait(int ms)
    {
        return m_event.timedwait(ms);
    }

    bool pop(UMediaPacket& packet, int ms)
    {
        if (empty())
        {
            timedwait(ms);
        }
        return pop(packet);
    }

    void cancelWait()
    {
        m_event.post();
    }

    int64_t getDuration()
    {
    	comn::AutoCritSec   lock(m_cs);

    	if (m_packets.size() < 2)
    	{
    		return 0;
    	}

    	return m_packets.back().pts() - m_packets.front().pts();
    }

    size_t dropUntilKeyFrame()
    {
    	comn::AutoCritSec   lock(m_cs);

    	size_t count = 0;
    	size_t toIdx = rfindVideoKey();
    	if (toIdx != (size_t)-1)
    	{
    		PacketDeque::iterator itBegin = m_packets.begin();
    		PacketDeque::iterator itEnd = itBegin;
    		std::advance(itEnd, toIdx);
    		m_packets.erase(itBegin, itEnd);
    		count = toIdx;
    	}
    	return count;
    }

    void enableEvent(bool enabled)
    {
        m_eventEnabled = enabled;
    }

    void unget(UMediaPacket& packet)
    {
        comn::AutoCritSec   lock(m_cs);
        m_packets.push_front(packet);
    }

};


UMediaPacketQueue::UMediaPacketQueue():
		m_pimp(new UMediaPacketQueuePrivate())
{
}

UMediaPacketQueue::~UMediaPacketQueue()
{
	delete m_pimp;
}


size_t UMediaPacketQueue::size()
{
    return m_pimp->size();
}

bool UMediaPacketQueue::empty()
{
	return m_pimp->empty();
}

size_t UMediaPacketQueue::push(UMediaPacket& packet)
{
    return m_pimp->push(packet);
}

UMediaPacket UMediaPacketQueue::pop()
{
    return m_pimp->pop();
}

bool UMediaPacketQueue::pop(UMediaPacket& packet)
{
	return m_pimp->pop(packet);
}

void UMediaPacketQueue::clear()
{
	m_pimp->clear();
}

bool UMediaPacketQueue::timedwait(int ms)
{
    return m_pimp->timedwait(ms);
}

bool UMediaPacketQueue::pop(UMediaPacket& packet, int ms)
{
    if (empty())
    {
        timedwait(ms);
    }
    return pop(packet);
}

void UMediaPacketQueue::cancelWait()
{
	m_pimp->cancelWait();
}

int64_t UMediaPacketQueue::getDuration()
{
	return m_pimp->getDuration();
}

size_t UMediaPacketQueue::dropUntilKeyFrame()
{
	return m_pimp->dropUntilKeyFrame();
}

void UMediaPacketQueue::enableEvent(bool enabled)
{
    m_pimp->enableEvent(enabled);
}

void UMediaPacketQueue::unget(UMediaPacket& packet)
{
    m_pimp->unget(packet);
}